package niseeImageGenerator;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.ArrayList;
import javax.imageio.ImageIO;

/**
 *
 * @author stussy
 */
public class ImageGenerator {

    public final int SQUARE = 1;
    public final int CIRCLE = 2;
    final int TRY_BEFORE_STOPPING = 999999; // change this to decrease the time used to generate an image 5*9
    final int NUMBER_OF_SHADES = 2; //max of 3
    public ArrayList<niShape> foreground1;
    public ArrayList<niShape> foreground2;
    /*
     *
     */

    public ImageGenerator() {
        foreground1 = new ArrayList<niShape>();
        foreground2 = new ArrayList<niShape>();
    }

    private int Total(ArrayList<Short> list) {
        int total = 0;
        for (int i : list) {
            total += i;
        }
        return total;
    }

    /**
     * Fills all whitespace in objectf with given ratio
     * @param objectf
     * @param format
     * @param percentages
     * @param sizes
     * @return
     */
    private ArrayList<niShape> FillObject(BufferedImage object, short format, ArrayList<Short> percentages, ArrayList<Short> sizes, short gap, ArrayList<Coordinates> coordinates, Color colour, boolean useShades) {
        ArrayList<niShape> mySquares = new ArrayList<niShape>();
        //perentages.size should be the same as sizes.size because they have a one to one ratio, and the total of all percentages should be at most 100
        if ((percentages.size() != sizes.size()) || (Total(percentages) > 100) || ((format!=SQUARE) &&(format!=CIRCLE))) {
            return mySquares;
        }

        int totalSize = coordinates.size();
        Graphics2D g2 = (Graphics2D) object.createGraphics();
        short currentFormat = format;
        for (int i = 0; i < sizes.size(); i++) {  //for each of the different sizes shapes try to add the shape until the percentage is reached
            short size = sizes.get(i).shortValue();
            short percentageToFill = percentages.get(i).shortValue();
            if (percentageToFill > 0 && size > 0) {
                double sizeD = size + (gap * 2); //the shape is larger than size because of white space around it
                double area;
                if (currentFormat == SQUARE) {
                    area = sizeD * sizeD;
                } else {
                    area = Math.PI * (Math.pow(sizeD / 2, 2));
                }

                double y;
                y = area / (totalSize / 100);// calculate percentage of area covered by shape
                double squaresToFill = 0;
                squaresToFill = percentageToFill / y;
                int squaresAdded = 0;
                Random gen = new Random();
                int missed = 0;
                Random colourGen = new Random();
                while (squaresAdded < squaresToFill && missed < TRY_BEFORE_STOPPING) {
                    int temp = Math.abs(gen.nextInt(coordinates.size() - 1));
                    int tx = ((Coordinates) coordinates.get(temp)).x;
                    int ty = ((Coordinates) coordinates.get(temp)).y;

                    int colourRand = 0;
                    if (useShades) {
                        colourRand = Math.abs(colourGen.nextInt(NUMBER_OF_SHADES));
                        colourRand--;
                    }
//                    System.out.println("Using format: "+format);
                    switch (currentFormat) {
                        case SQUARE:
                            niSquare mySquare = new niSquare(tx, ty, size);
                            if (!mySquare.checkDrawSquare(gap, object, g2, colour, colourRand)) {
                                missed++;
                            } else {
                                missed = 0;
                                //add to array
                                mySquares.add(mySquare);
                                squaresAdded++;
                            }
                            break;
                        case CIRCLE:
                            niCircle myCircle = new niCircle(tx, ty, size);
                            if (!myCircle.checkDrawCircle(gap, object, g2, colour, colourRand)) {
                                missed++;
                            } else {
                                missed = 0;
                                //add to array
                                mySquares.add(myCircle);
                                squaresAdded++;
                            }
                            break;
                    }
                }

                if (missed >= TRY_BEFORE_STOPPING) {
                    System.out.println(TRY_BEFORE_STOPPING + " collisions");
                }
            }
        }
        return mySquares;
    }

    /**
     * Inverts objectf to fill foreground
     * @param objectf
     * @return list of Circle/Square objects which represent the foreground
     */
    public boolean FillForeground(File objectf, short format, ArrayList<Short> percentages, ArrayList<Short> sizes, short gapSize, int numberOfForegrounds) {
        try {
            BufferedImage buff = ImageIO.read(objectf);
            if (numberOfForegrounds > 0) {
                ArrayList<Coordinates> coordinates1 = new ArrayList<Coordinates>();
                Invert(buff, coordinates1, 1);
                if (coordinates1.size() > 0) {
                    foreground1 = FillObject(buff, format, percentages, sizes, gapSize, coordinates1, Color.black, false);
                    if (numberOfForegrounds == 1) {
                        return true;
                    }
                }
            }
            if (numberOfForegrounds > 1) {
                ArrayList<Coordinates> coordinates2 = new ArrayList<Coordinates>();
                Invert(buff, coordinates2, 2);
                if (coordinates2.size() > 0) {
                    foreground2 = FillObject(buff, format, percentages, sizes, gapSize, coordinates2, Color.black, false);
                    if (numberOfForegrounds == 2)//?
                    {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            //objectf file not found?
        }
        return false;
    }

    /*
     * @param object - An ArrayList of niSquare or niCircle objects representing the foreground of the image - retrieved with fillObject()
     * @param format - 1 = squares, 2 = circles
     * @param percentages
     * @param sizes
     * @param gapSize
     * @param foregroundColour
     * @param backgroundColour
     * @return A BufferedImage object of the final NiSee image
     */
    public BufferedImage CreateImage(ArrayList<niShape> foreground1, ArrayList<niShape> foreground2, short format, ArrayList<Short> percentages, ArrayList<Short> sizes, short gapSize, Color foregroundColour1, Color foregroundColour2, Color backgroundColour) {
        BufferedImage bufferedImage = new BufferedImage(309, 309, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < 309; x++) {
            for (int y = 0; y < 309; y++) {
                bufferedImage.setRGB(x, y, Color.white.getRGB());
            }
        }
        /* if ((foreground1 == null) && (foreground2 == null)) {
        System.out.println("Both foregrounds are null");
        return bufferedImage;
        }*/
        if (foreground1 != null) {
            if ((foreground1.size() > 0) && (foregroundColour1 != null)) {
                AddForeground(foreground1, format, foregroundColour1, bufferedImage);
            }
        }
        if (foreground2 != null) {
            if ((foreground2.size() > 0) && (foregroundColour2 != null)) {
                AddForeground(foreground2, format, foregroundColour2, bufferedImage);
            }
        }
        //get ArrayList of valid coordinates
        ArrayList<Coordinates> coordinates = getCoordinates(bufferedImage);

        FillObject(bufferedImage, format, percentages, sizes, gapSize, coordinates, backgroundColour, true);
        return bufferedImage;
    }

    private void AddForeground(ArrayList<niShape> foreground, int format, Color foregroundColour, BufferedImage img) {
        Graphics2D g2 = (Graphics2D) img.createGraphics();
        g2.setColor(Color.BLACK);
        Random colourGen = new Random();
        int colourRand;
        switch (format) {
            case SQUARE:
                for (int i = 0; i < foreground.size(); i++) {
                    colourRand = Math.abs(colourGen.nextInt(NUMBER_OF_SHADES));
                    colourRand--;
                    ((niSquare) foreground.get(i)).setColour(foregroundColour);
                    ((niSquare) foreground.get(i)).drawSquare(g2, colourRand);
                }
                break;
            case CIRCLE:
                for (int i = 0; i < foreground.size(); i++) {
                    colourRand = Math.abs(colourGen.nextInt(NUMBER_OF_SHADES));
                    colourRand--;
                    ((niCircle) foreground.get(i)).setColour(foregroundColour);
                    ((niCircle) foreground.get(i)).drawCircle(g2, colourRand);
                }
                break;
        }
    }

    private static void Invert(BufferedImage tshape, ArrayList<Coordinates> coordinates, int foregroundNum) {
        for (int i = 0; i < tshape.getWidth(); i++) {
            for (int j = 0; j < tshape.getHeight(); j++) {
                if (foregroundNum == 1) {
                    if (tshape.getRGB(i, j) == Color.black.getRGB()) {
                        coordinates.add(new Coordinates(i, j));
                        tshape.setRGB(i, j, Color.white.getRGB());
                    } else {
                        if (tshape.getRGB(i, j) == Color.white.getRGB()) {
                            tshape.setRGB(i, j, Color.black.getRGB());
                        }
                    }
                } else {
                    if ((tshape.getRGB(i, j) != Color.black.getRGB()) && ((tshape.getRGB(i, j) != Color.white.getRGB()))) {
                        coordinates.add(new Coordinates(i, j));
                        tshape.setRGB(i, j, Color.white.getRGB());
                    } else {
                        tshape.setRGB(i, j, Color.black.getRGB());
                    }
                }
            }
        }
    }

    private static ArrayList<Coordinates> getCoordinates(BufferedImage tshape) {
        ArrayList<Coordinates> coordinates = new ArrayList<Coordinates>();
        for (int i = 0; i < tshape.getWidth(); i++) {
            for (int j = 0; j < tshape.getHeight(); j++) {
                if (tshape.getRGB(i, j) == Color.white.getRGB()) {
                    coordinates.add(new Coordinates(i, j));
                }

            }
        }
        return coordinates;
    }
}
